package com.sooncode.project.focusservice.service;

import com.alibaba.excel.EasyExcel;
import com.sooncode.project.core.finder.Finder;
import com.sooncode.project.core.finder.OType;
import com.sooncode.project.core.model.DomainException;
import com.sooncode.project.focusservice._.infrastructure.RepositoryFactory;
import com.sooncode.project.focusservice._.infrastructure.file.IStoreFile;
import com.sooncode.project.focusservice.model.*;
import com.sooncode.project.focusservice.model.VO.FileObject;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Service
public class DownloadCourseService {
    @Autowired
    RepositoryFactory factory;

    public void downloadCheckCourse(HttpServletResponse response){
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        try {
            String fileName = URLEncoder.encode("课程检查", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
            EasyExcel.write(response.getOutputStream(),  CourseCheckExcel.class).inMemory(true)
                    .sheet("课程表").doWrite(()->new CourseCheckExcel().export());
        }catch (Exception ex){
            ex.printStackTrace();
            throw new DomainException("导出文件时发生错误");
        }
    }

    public void downloadCourse(String testId,String reTestId,String phase, HttpServletResponse response){
        IStoreFile storeFile=factory.getStoreFile();
        Test test=new Finder<>(Test.class).byId(testId);
        Member member=new Finder<>(Member.class).byId(test.getMemberId());
        member.addDownloadCourse();
        Organization org=new Finder<>(Organization.class).byId(test.getOrgId());
        Test reTest=test;
        int phaseInt=Integer.parseInt(phase);
        if(phaseInt!=1){
            reTest=new Finder<>(Test.class).byId(reTestId);
        }
        if(!reTest.isDownloadCourse()){
            org.handlerDownloadCourse();
        }
        reTest.doDownloadCourse();
        try(ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
            response.setContentType("application/x-msdownload");
            response.setCharacterEncoding("utf-8");
            String fileName = URLEncoder.encode(test.getName() + "的课程文件", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".zip");
            Map<String,Map> fileMap=getFileMap(test,reTest,phaseInt);
            for (String phaseName : fileMap.keySet()) {
                ZipEntry folder=new ZipEntry(phaseName+"/");
                zipOut.putNextEntry(folder);
                zipOut.closeEntry();
                //生成课程表
                String[][] timetableArr=generateTimetable(reTest);
                Map<String,List> listMap=fileMap.get(phaseName);
                List<CourseFileHelper> fileList=new ArrayList<>();
                for (String type : listMap.keySet())
                    fileList.addAll(listMap.get(type));
                Collections.sort(fileList);
                //填充课程表
                List<FileObject> timetableFiles= handleCourseFile(timetableArr,fileList);
                //打包课程表
                ByteArrayOutputStream timetableOS = new ByteArrayOutputStream();
                zipOut.putNextEntry(new ZipEntry(phaseName+"/专注力课程表"+".xlsx"));
                EasyExcel.write(timetableOS,  TimetableExport.class)
                        .registerWriteHandler(new TimetableExport(test.getName(),test.getSex(),test.getShowAge()))
                        .sheet("课程表")
                        .doWrite(() -> {
                            return new TimetableExport().export(timetableArr,phaseName);
                        });
                timetableOS.writeTo(zipOut);
                zipOut.closeEntry();
                //ZipEntry typeFolder=new ZipEntry(phaseName+"/"+type+"/");
                //zipOut.putNextEntry(typeFolder);
                //zipOut.closeEntry();
                //打包课程文件
                for(FileObject file:timetableFiles){
                    if(StringUtils.isEmpty(file.getId())||StringUtils.isEmpty(file.getType())) continue;
                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                    zipOut.putNextEntry(new ZipEntry(phaseName+"/"+file.getName()+file.getType().substring(file.getType().lastIndexOf('.'))));
                    storeFile.GetFileStream(file.getId(),outputStream);
                    outputStream.writeTo(zipOut);
                    zipOut.closeEntry();
                }
                //加入感统
                addGanTong(zipOut,phase,phaseName,test);
            }
        }catch (DomainException ex){
            throw ex;
        }
        catch (Exception ex){
            throw new DomainException("导出文件时发生错误");
        }
    }
    //加入感统
    private void addGanTong(ZipOutputStream zipOut,String phase,String phaseName,Test test) throws IOException {
        String resourceFile="/"+phase+".xlsx";
        InputStream inputStream = getClass().getResourceAsStream(resourceFile);
        ByteArrayOutputStream gantongFile = new ByteArrayOutputStream();
        Map map=new HashMap();
        map.put("name",test.getName());
        map.put("sex",test.getSex());
        map.put("age",test.getShowAge());
        EasyExcel.write(gantongFile)
                .withTemplate(inputStream)
                .sheet().doFill(map);
        zipOut.putNextEntry(new ZipEntry(phaseName+"/感统课程表.xlsx"));
        gantongFile.writeTo(zipOut);
        zipOut.closeEntry();

    }
    //生成课程表
    private String[][] generateTimetable(Test reTest){
        Map<String,String[]> codeMap=new LinkedHashMap<>();
        codeMap.put("听觉基础", new String[]{"1", "2", "3", "4"});
        codeMap.put("视觉基础", new String[]{"5", "6", "7", "8","9"});
        Map<String,Integer> stdMap=new HashMap<>();
        stdMap.put("听觉基础", 12);
        stdMap.put("视觉基础", 10);
        int typeIndex=0;
        String[][] timetableArr=new String[24][4];
        for(Map.Entry<String,String[]> code:codeMap.entrySet()){
            List<TestData> list=reTest.getData().stream().filter(f-> Arrays.asList(code.getValue()).contains(f.getItem_no())).collect(Collectors.toList());
            double sum=0;
            int count=0;
            for(TestData d:list){
                sum+=d.getLevelScore();
                count++;
            }
            double avg=sum/count;
            int std=stdMap.get(code.getKey());
            //计算各纬度应该排的课程数量,并把课程数量放入队列, 如果课程数量不够48节课,就在最少的类别上补上缺少的数量
            LinkedList<AbstractMap.SimpleEntry<String,Integer>> amountQueue=new LinkedList<>();
            int sumAmount=0;
            int minAmount=100;
            AbstractMap.SimpleEntry<String,Integer> minCodeEntry=null;
            for(TestData d:list){
                double amount = std-(d.getLevelScore()-avg)/avg*std;
                amount=amount>=0?amount:std;
                int amountInt=(int)Math.round(amount);
                AbstractMap.SimpleEntry<String,Integer> codeEntry=new AbstractMap.SimpleEntry<>(d.getItem_no(),amountInt);
                amountQueue.add(codeEntry);
                sumAmount+=amountInt;
                if(amountInt<minAmount){
                    minAmount=amountInt;
                    minCodeEntry=codeEntry;
                }
            }
            if(sumAmount<48)
                minCodeEntry.setValue(minCodeEntry.getValue()+48-sumAmount);

            //按照课程数量队列进行排课
            for(int i=0;i<timetableArr.length;i++){
                if(amountQueue.isEmpty()) break;
                for(int j=0;j<2;j++){
                    if(amountQueue.isEmpty()) break;
                    AbstractMap.SimpleEntry<String,Integer> codeEntry=amountQueue.remove();
                    timetableArr[i][typeIndex*2+j]=codeEntry.getKey();
                    if(codeEntry.getValue()-1>0) {
                        codeEntry.setValue(codeEntry.getValue() - 1);
                        amountQueue.add(codeEntry);
                    }
                }
            }
            typeIndex++;
        }
        return timetableArr;
    }
    //处理课程文件
    private List<FileObject> handleCourseFile(String[][] timetableArr,List<CourseFileHelper> fileList){
        List<FileObject> fileObjects=new ArrayList<>();
        Map<String,Integer> fileRepeat=new HashMap<>();
        for(int i=0;i<timetableArr.length;i++)
            for(int j=0;j<timetableArr[i].length;j++){
                String code=timetableArr[i][j];
                //timetableArr[i][j]=""; //todo 取消显示编号

                if(fileRepeat.containsKey(code))
                    fileRepeat.put(code,fileRepeat.get(code)+1);
                else
                    fileRepeat.put(code,0);
                List<CourseFileHelper> list=fileList.stream().filter(f->f.getTestCode().equals(code)).collect(Collectors.toList());
                if(list==null||list.size()==0)
                    continue;
                //循环获取文件
                CourseFileHelper cfh=list.get(fileRepeat.get(code)%list.size());
                timetableArr[i][j]=cfh.getFileName()+"#"+cfh.getMeasure();
                List<FileObject> sourceFiles=cfh.getFiles();
                if(sourceFiles==null||sourceFiles.size()==0)continue;
                List<FileObject> files = sourceFiles.stream().filter(f->
                        !fileObjects.stream().anyMatch(file -> file.getId().equals(f.getId())))
                        .collect(Collectors.toList());
                if(files.size()==0) files=sourceFiles;
                int number=0;
                //随机选择文件
                if(files.size()>1) {
                    Random random = new Random();
                    number = random.nextInt(files.size() - 1);
                }
                FileObject file = files.get(number);
                FileObject fo=new FileObject();
                fo.setId(file.getId());
                fo.setName((i+1)+"-"+(j+1));
                fo.setType(file.getType());
                fileObjects.add(fo);
            }
        return fileObjects;
    }
    //获取文件列表
    private Map<String,Map> getFileMap(Test test,Test reTest,int phaseInt) {
        List<CourseLatitude> latitudeList = new Finder<>(CourseLatitude.class).list();
        int age=Integer.parseInt(Test.getTestAge(test.getAge()));
        if(age<2)age=2;
        if(age>12)age=12;
        List<CourseSetting> courseSettingList = new Finder<>(CourseSetting.class, false)
                .byField("age", age+"")
                .and("minScore", test.getAverageScore(), OType.lte)
                .and("maxScore", test.getAverageScore(), OType.gte)
                .list();
        Map<String, Map> fileMap = new HashMap<>();
        for (CourseSetting setting : courseSettingList) {
            if (setting.getItems().size() < phaseInt) continue;
            CourseSettingItem item = setting.getItems().get(phaseInt - 1);
            for (String s : item.getCourses()) {
                //获取课程
                Course course = new Finder<>(Course.class).byId(s);
                if (course == null){
                    System.out.println(course.getJoinName()+"课程未找到");
                    continue;
                }

                CourseLatitude latitude = latitudeList.stream()
                        .filter(f -> f.getId().equals(course.getLatitudeId()))
                        .findFirst().orElse(null);
                if (latitude == null) {
                    System.out.println(course.getJoinName()+":课程纬度不正确");
                    continue;
                }
                //根据复测数据获取课程
                TestData testData = reTest.getData().stream()
                        .filter(f -> f.getItem_no().equals(latitude.getTestItemCode()))
                        .findFirst().orElse(null);
                CourseFileHelper helper = new CourseFileHelper();
                helper.setFileName(course.getJoinName());
                if(course.getMeasure()!=null)
                    helper.setMeasure(String.join(",",course.getMeasure()));
                helper.setDifficulty(course.getDifficulty());
                helper.setTestCode(testData.getItem_no());
                //如果未测试, 下载课程时按照0分处理, 一样可以出课程
                double score = testData.getItem_score()<0?0:testData.getItem_score();
                //处理文件
                if(course.getFiles()!=null&&course.getFiles().size()>0) {
                    CourseFile file = course.getFiles().stream()
                            .filter(f -> f.getMinScore() <= score
                                    && f.getMaxScore() >= score)
                            .findFirst().orElse(null);
                    if (file != null) {
                        helper.setFileId(file.getId());
                        helper.setFileType(file.getFileType());
                        helper.setFiles(file.getFiles());
                    }
                }
                if (!fileMap.containsKey(item.getPhase()))
                    fileMap.put(item.getPhase(), new HashMap<>());
                Map<String, List> phaseMap = fileMap.get(item.getPhase());
                if (!phaseMap.containsKey(setting.getType()))
                    phaseMap.put(setting.getType(), new ArrayList<CourseFile>());
                phaseMap.get(setting.getType()).add(helper);
            }
        }
        return fileMap;
    }
    @Data
    private class CourseFileHelper implements Comparable<CourseFileHelper> {
        private String fileName;
        private String fileType;
        private String fileId;
        private String testCode;
        private double difficulty;
        private String measure="";
        private List<FileObject> files;

        @Override
        public int compareTo(CourseFileHelper o) {
            return Double.compare(this.getDifficulty(), o.getDifficulty());
        }
    }
}
